package future

import (
	"github.com/618lf/swakx-go/pkg/lock"
	"github.com/618lf/swakx-go/pkg/tools/panics"
	"sync"
)

type Promise interface {
	Then(Resolve) Promise
	Catch(Reject) Promise
}

type promise struct {
	Promise
	done        chan struct{}
	res         interface{}
	err         error
	promiseFunc PromiseFunc
	thenFunc    Resolve
	catchFunc   Reject
	lock        sync.Locker
}

func (p *promise) isDone() bool {
	select {
	case <-p.done:
		return true
	default:
		return false
	}
}

func (p *promise) complete() {
	defer panics.CatchPanic()
	if p.isDone() {
		return
	}
	close(p.done)
	var (
		resolve Resolve
		reject  Reject
	)
	p.lock.Lock()
	resolve = p.thenFunc
	reject = p.catchFunc
	p.lock.Unlock()

	if resolve != nil && p.err != nil {
		resolve(p.res)
	}

	if reject != nil && p.err == nil {
		reject(p.err)
	}
}

func (p *promise) exec() {
	defer panics.CatchPanicWithHandleError(func(err error) {
		p.err = err
		p.complete()
	})
	p.promiseFunc(func(rsp interface{}) {
		p.res = rsp
		p.complete()
	}, func(err error) {
		p.err = err
		p.complete()
	})
}

func (p *promise) Then(f Resolve) Promise {
	if p.isDone() {
		if p.err == nil {
			f(p.res)
		}
	} else {
		p.lock.Lock()
		p.thenFunc = f
		p.lock.Unlock()
	}
	return p
}

func (p *promise) Catch(f Reject) Promise {
	if p.isDone() {
		if p.err != nil {
			f(p.err)
		}
	} else {
		p.lock.Lock()
		p.catchFunc = f
		p.lock.Unlock()
	}
	return p
}

type Resolve func(rsp interface{})
type Reject func(err error)
type PromiseFunc func(Resolve, Reject)

func NewPromise(pf PromiseFunc) Promise {
	p := &promise{
		done:        make(chan struct{}),
		promiseFunc: pf,
		lock:        lock.NewSpinLock(),
	}
	// 执行 pf
	p.exec()
	return p
}
